package com.atguigu.srb.core.service.impl;

import com.alibaba.fastjson.JSON;
import com.atguigu.guli.common.util.IpUtils;
import com.atguigu.guli.common.util.MD5;
import com.atguigu.guli.common.util.RegexValidateUtils;
import com.atguigu.srb.base.exception.SrbException;
import com.atguigu.srb.base.result.ResponseEnum;
import com.atguigu.srb.base.util.JwtUtils;
import com.atguigu.srb.base.util.SrbAssert;
import com.atguigu.srb.core.hfb.RequestHelper;
import com.atguigu.srb.core.pojo.entity.UserAccount;
import com.atguigu.srb.core.pojo.entity.UserBind;
import com.atguigu.srb.core.pojo.entity.UserInfo;
import com.atguigu.srb.core.mapper.UserInfoMapper;
import com.atguigu.srb.core.pojo.entity.UserLoginRecord;
import com.atguigu.srb.core.pojo.vo.LoginVO;
import com.atguigu.srb.core.pojo.vo.RegistVO;
import com.atguigu.srb.core.pojo.vo.UserIndexVO;
import com.atguigu.srb.core.pojo.vo.UserInfoSearchVO;
import com.atguigu.srb.core.service.UserAccountService;
import com.atguigu.srb.core.service.UserBindService;
import com.atguigu.srb.core.service.UserInfoService;
import com.atguigu.srb.core.service.UserLoginRecordService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.util.Map;
import java.util.UUID;

/**
 * <p>
 * 用户基本信息 服务实现类
 * </p>
 *
 * @author muzhao
 * @since 2024-08-19
 */
@Service
@Slf4j
public class UserInfoServiceImpl extends ServiceImpl<UserInfoMapper, UserInfo> implements UserInfoService {
    //此处的模板一定要和sms服务保存验证码到redis的模板类对象类型一样
    @Resource
    StringRedisTemplate stringRedisTemplate;
    @Resource
    UserLoginRecordService userLoginRecordService;

    @Resource
    UserBindService userBindService;

    @Resource
    UserAccountService userAccountService;

    @Override
    public void saveUser(RegistVO registVO) {
        //1.检验注册参数
        String code = registVO.getCode();
        Integer userType = registVO.getUserType();
        String password = registVO.getPassword();
        String mobile = registVO.getMobile();
        SrbAssert.assertNotTrue(StringUtils.isEmpty(code) ||
                        StringUtils.isEmpty(mobile) ||
                        StringUtils.isEmpty(password) ||
                        userType == null,
                ResponseEnum.PARAM_IS_NULL_ERROR);
        SrbAssert.assertTrue(RegexValidateUtils.checkCellphone(mobile)
                , ResponseEnum.MOBILE_ERROR);

        //2.校验验证码：使用注册手机号拼接key在redis中查询验证码和注册提交的验证码比较 不一致则抛异常
        String redisCode = stringRedisTemplate.opsForValue().get("sms:code:" + 1 + ":" + mobile);//1表示此次手机号为注册
        SrbAssert.assertTrue(code.equals(redisCode), ResponseEnum.CODE_ERROR);
        //删除使用后的验证码
        stringRedisTemplate.delete("sms:code:" + 1 + ":" + mobile);

        //3.重复注册校验：使用注册手机号查询数据 若存在 抛出异常（发送验证码时验证手机号是否已被占用）
//        SrbAssert.assertTrue(this.count(Wrappers.lambdaQuery(UserInfo.class)
//                .eq(UserInfo::getMobile, mobile)) == 0, ResponseEnum.MOBILE_EXIST_ERROR);

        //4.密码加密：MD5工具类对注册密码加密保存(预防数据库数据泄露)
        // MD5加盐 加密 防止暴力破解：每个用户注册时生成一个salt。MD5(MD5(明文密码)+salt)
        //用户登录：明文账号密码提交到后端后 使用账号查询用户的信息 对明文密码和盐加密 得到的密文与数据库密文进行比对
        //生成盐
        String salt = UUID.randomUUID().toString().substring(0, 6);
        //加密
        String encrypt = MD5.encrypt(MD5.encrypt(password) + salt);

        //5.保存信息到数据库
        UserInfo userInfo = new UserInfo();
        userInfo.setPassword(encrypt);
        userInfo.setSalt(salt);
        userInfo.setMobile(mobile);
        userInfo.setUserType(userType);
        userInfo.setHeadImg("https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif");
        userInfo.setIntegral(0);//初始化用户积分
        userInfo.setNickName("融宝" + UUID.randomUUID().toString().substring(0, 8));
        this.save(userInfo);

    }

    @Override
    public String login(LoginVO loginVO, HttpServletRequest request) {
        String mobile = loginVO.getMobile();
        String password = loginVO.getPassword();
        Integer userType = loginVO.getUserType();
        SrbAssert.assertNotTrue(StringUtils.isEmpty(mobile) ||
                        StringUtils.isEmpty(password) ||
                        userType == null,
                ResponseEnum.PARAM_IS_NULL_ERROR);
        //1.根据账号查询用户数据：若没有账号 代表不存在
        UserInfo userInfo = this.getOne(Wrappers.lambdaQuery(UserInfo.class)
                .eq(UserInfo::getMobile, mobile)
                .eq(UserInfo::getUserType,userType));
        SrbAssert.assertNotNull(userInfo,ResponseEnum.LOGIN_MOBILE_ERROR);

        //2.对比密码：比较失败 密码错误
        String salt = userInfo.getSalt();
        String encrypt = MD5.encrypt(MD5.encrypt(password) + salt);
        SrbAssert.assertTrue(encrypt.equals(userInfo.getPassword()),ResponseEnum.LOGIN_PASSWORD_ERROR);

        //3.登录成功 创建token返回
        //保存登录日志
        userLoginRecordService.saveLoginLogs(request, userInfo,1);

        return JwtUtils.createToken(userInfo.getId(),userInfo.getName());
    }

    @Override
    public Page<UserInfo> getUserInfos(UserInfoSearchVO userInfoSearchVO,Page<UserInfo> page) {
        LambdaQueryWrapper<UserInfo> wrapper = Wrappers.lambdaQuery(UserInfo.class);
        String mobile = userInfoSearchVO.getMobile();
        Integer status = userInfoSearchVO.getStatus();
        Integer userType = userInfoSearchVO.getUserType();
        if (StringUtils.isNotEmpty(mobile)) {
            //使用手机号模糊查询
            wrapper.like(UserInfo::getMobile, mobile);
        }
        if (status!=null){
            wrapper.eq(UserInfo::getStatus, status);
        }
        if (userType!=null){
            wrapper.eq(UserInfo::getUserType, userType);
        }
        this.page(page,wrapper);
        return page;
    }

    @Override
    public boolean isBindMobile(String token) {
        Long userId = JwtUtils.getUserId(token);
        UserInfo userInfo = this.getById(userId);
        return StringUtils.isNotEmpty(userInfo.getMobile());
    }

    @Override
    public void bindMobile(String token, RegistVO registVO) {
        Long userId = JwtUtils.getUserId(token);
        SrbAssert.assertTrue(RegexValidateUtils.checkCellphone(registVO.getMobile()),ResponseEnum.MOBILE_ERROR);
        try {
            UserInfo userInfo = new UserInfo();
            userInfo.setId(userId);
            userInfo.setMobile(registVO.getMobile());
            userInfo.setUserType(registVO.getUserType());
            //这里只有传入一个userInfo才会出发属性的自动填充
            this.updateById(userInfo);
        } catch (Exception e) {
            throw new SrbException(ResponseEnum.MOBILE_EXIST_ERROR);
        }
    }

    @Override
    public UserIndexVO getUserIndexVO(String token) {
        Long userId = JwtUtils.getUserId(token);

        return baseMapper.selectUserIndexVOByUserId(userId);
    }

    @Transactional
    @Override
    public String hfbNotify(HttpServletRequest request) {
        Map<String, String[]> parameterMap = request.getParameterMap();
        Map<String, Object> params = RequestHelper.switchMap(parameterMap);
        // 判断如果绑定成功
        // sign：签名 防止数据被篡改 校验数据安全
        boolean signEquals = RequestHelper.isSignEquals(params);
        if (!signEquals) {
            //签名验证不通过
            log.error("签名校验失败：{}", JSON.toJSONString(params));
            return "fail";
        }
        // resultCode:0001 代表绑定成功 resultMsg
        String resultCode = params.get("resultCode").toString();
        if (!"0001".equals(resultCode)){
            log.error("绑定失败：{}",JSON.toJSONString(params));
            return "fail";
        }
        // agentUserId：绑定hfb的用户id
        String agentUserId = params.get("agentUserId").toString();
        // bindCode：用户在汇付宝中的账户id
        String bindCode = params.get("bindCode").toString();
        //更新数据库表：给agentUserId对应用户 绑定bindCode到数据库中

        UserBind userBind = userBindService.getOne(Wrappers.lambdaQuery(UserBind.class)
                .eq(UserBind::getUserId, agentUserId));
        userBind.setStatus(1);//1为绑定成功
        userBind.setBindCode(bindCode);
        userBindService.updateById(userBind);
        //更新用户表中的信息
        UserInfo userInfo = this.getById(agentUserId);
        userInfo.setIdCard(userBind.getIdCard());
        userInfo.setBindStatus(1);
        userInfo.setName(userBind.getName());
        userInfo.setBindCode(bindCode);
        this.updateById(userInfo);

        //为绑定hfb成功的会员开通个人账户
        UserAccount userAccount = new UserAccount();
        userAccount.setUserId(userInfo.getId());
        userAccount.setAmount(new BigDecimal(0));
        userAccount.setFreezeAmount(new BigDecimal(0));
        userAccountService.save(userAccount);

        return "success";//hfb需要success给出成功的应答
    }

    public UserInfo checkUserStatus(String token) {
        //1、验证用户是否登录
        Long userId = JwtUtils.getUserId(token);
        //2、验证用户的状态:
        UserInfo userInfo = this.getById(userId);
        //  2.1 用户是否被锁定
        SrbAssert.assertNotTrue(userInfo.getStatus()==0, ResponseEnum.LOGIN_LOKED_ERROR);
        //  2.2 用户是否绑定hfb
        SrbAssert.assertNotTrue(userInfo.getBindStatus()!=1, ResponseEnum.USER_NO_BIND_ERROR);
        // 2.3 借款人审核状态
        SrbAssert.assertNotTrue(userInfo.getBorrowAuthStatus()!=2,ResponseEnum.USER_NO_AMOUNT_ERROR);
        return userInfo;
    }

}
